# pascal language lexer+parser to use with packcc parser generator
%prefix "pascal"

%source{

#include <stdio.h>

static const char *dbg_str[] = { "Evaluating rule", "Matched rule", "Abandoning rule" };

#define PCC_DEBUG(event, rule, level, pos, buffer, length) \
    fprintf(stdout, "%*s%s %s @%d [%.*s]\n", (int)(level * 2), "", dbg_str[event], rule, (int)pos, (int)length,  buffer); fflush(stdout)

    /* NOTE: To guarantee the output order, stderr, which can lead a race condition with stdout, is not used. */

}


start <- program / unit _ !.

program	<-  "program" __ identifier ";" _ block "."

unit	<-  "unit" __ identifier ";" _ "interface" _ interface_part _ "implementation" _ implementation_part _ "end" "." 

uses	<-  "uses" _ identifier ("," _ identifier)* ";" _

block	<-  declarations "begin" _ statements _ "end"

statements	<-  (statement ";" _)*

declarations	<-  declaration_part*

declaration_part	<-  uses / types / constants / vars / procedure_declaration / function_declaration


interface_part	<-  (uses / types / constants / vars / procedure_header / function_header)*

implementation_part	<-  declarations


procedure_header	<-  "procedure" _ identifier "(" argument_list_declaration ")" _ ";" _ 
  / "procedure" _ identifier _ ";" _ 

procedure_declaration	<-  procedure_header _ block ";" _ 

function_header	<-  "function" _ identifier "(" argument_list_declaration ")" _ ":" _ type ";" _
  / "function" _ identifier _ ":" _ type _ ";" _ 

function_declaration	<-  function_header _ block ";" _ 

argument_list_declaration	<-  argument_declaration? (";" _ argument_declaration)* 

argument_declaration	<-  "var"? _ identifier ("," _ identifier)* ":" _ type


types	<-  "type" _ type_declaration+ 

type_declaration	<-  identifier _ "=" _ type _ ";" _ 

constants	<-  "const" _ constant+ 

constant	<-  identifier _ "=" _ const_literal _ ";" _ 
  / identifier _ ":" _ type _ "=" _ const_literal _ ";" _ 

const_literal	<-  literal
  / "(" _ argument_list _ ")" 
  / "(" _ field_literal (";" _ field_literal)* ")"

field_literal	<-  identifier _ ":" _ literal 


vars	<-  "var" _ var_declaration+

var_declaration	<-  identifier ("," _ identifier)* ":" _ type _ ";" _ 

type	<-  "array" _ "[" integer_literal _ ".." _ integer_literal _ "]" _ "of" _ type
  / "record" __ argument_list_declaration _ ";"? _ "end"
  / "^" type
  / identifier

statement	<-  compound / procedure_call / assignment / if_else / if / for / while / repeat / with

compound	<-  "begin" _ statements _ "end"

assignment	<-  lvalue _ ":=" _ expression 

lvalue	<-  field_access / array_accesses

field_access	<-  array_accesses "." lvalue

array_accesses	<-  array_access / identifier

array_access	<-  identifier "[" expression "]"

procedure_call	<-  identifier _ "(" argument_list ")"

argument_list	<-  argument? ("," _ argument)*

argument	<-  expression

if_else	<-  "if" _ expression _ "then" _ statement _ "else" _ statement

if	<-  "if" _ expression _ "then" _ statement

for	<-  "for" _ identifier _ ":=" _ expression _ ("to" / "downto") _ expression _ "do" _ statement

while	<-  "while" _ expression _ "do" _ statement

repeat	<-  "repeat" _ statements _ "until" _ expression

with	<-  "with" __ lvalue __ "do" __ statement

function_call	<-  identifier _ "(" argument_list ")" 

expression	<-  negation / comparision / or_expr

negation	<-  "not" _ expression

comparision	<-  or_expr _ "=" _ or_expr
  / or_expr _ "<>" _ or_expr
  / or_expr _ ">=" _ or_expr
  / or_expr _ "<=" _ or_expr
  / or_expr _ ">" _ or_expr
  / or_expr _ "<" _ or_expr

or_binary	<-  and_expr _ ("or" / "+" / "-") _ or_expr

or_expr	<-  or_binary / and_expr

and_binary	<-  base_expr _ ("and" / "*" / "/" / "div" / "mod") _ and_expr

and_expr	<-  and_binary / base_expr

base_expr	<-  primary / nested_expression

nested_expression 	<-  "(" _ expression _ ")"

primary	<-  function_call / pointer_to / deref / literal / lvalue

pointer_to	<-  "@" identifier

deref	<- identifier "^"

identifier	<-  [A-Za-z][A-Za-z0-9_]*


literal	<-  string_literal / boolean_literal / real_literal / integer_literal

string_literal	<-  "'" string_character* "'"

string_character	<-  !("'") .

boolean_literal	<-  "true" / "false"

integer_literal	<-  "$" [0-9A-Fa-f]+
	/ [0-9]+

real_literal	<-  [0-9]+ "." [0-9]+

comment	<-  "{" comment_character* "}"

comment_character <-  !("}") .

_ 	<-  (comment / [ \t\n\r])*

__ 	 <-  (comment / [ \t\n\r])+


%%

int main() {
    pascal_context_t *ctx=pascal_create(NULL);
    while (pascal_parse(ctx, NULL)){;}
    pascal_destroy(ctx);
    return 0;
}

